﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MapInfo.MiPro.Interop;
using System.IO;
using System.Xml.Linq;
using System.Threading;
using System.Globalization;

namespace MapWindowDocking
{
    public partial class WininfoControl : UserControl
    {
        private readonly MapinfoWrapper mapinfo;

        public WininfoControl(MapinfoWrapper Mapinfo, Window associatedWindow)
        {
            InitializeComponent();
            this.mapinfo = Mapinfo;
            this.AssociatedWindow = associatedWindow;
            this.AssociatedWindow.WindowChanged += new Action<Window>(AssociatedWindow_WindowChanged);
            Globals.Favourites.FavouritesUpdated += new EventHandler(Favourites_FavouritesUpdated);
            this.LoadFavourites();
        }


        /// <summary>
        /// Called when the favourites collection has been updated.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Favourites_FavouritesUpdated(object sender, EventArgs e)
        {
            this.LoadFavourites();
        }

        void AssociatedWindow_WindowChanged(Window obj)
        {
            this.UpdateControlsWithValues();
        }

        /// <summary>
        /// Gets or sets the associated mapinfo window that owns this lengend.
        /// </summary>
        public Window AssociatedWindow { get; set; }

        /// <summary>
        /// Loads the favourites from the favourites xml and adds them to the favourites menu.
        /// </summary>
        public void LoadFavourites()
        {
            IEnumerable<Favourite> favs = Globals.Favourites.Items;
            
            this.splitButton1.SplitMenu = new ContextMenu();
            foreach (var favourite in favs)
            {
                MenuItem item = new MenuItem();
                item.Text = favourite.Name;
                item.Tag = favourite;

                item.Click += new EventHandler(favouriteClicked);

                this.splitButton1.SplitMenu.MenuItems.Add(item);
            }
        }

        public void SetCenterTextBoxValues(double x, double y)
        {
            this.XCoordTextBox.Text = String.Format("{0:n}", x);
            this.YCoordTextBox.Text = String.Format("{0:n}", y);
        }

        public void SetZoomTextBoxValue(double zoomWidth)
        {
            this.zoomTextbox.Text = String.Format("{0:n}",zoomWidth);
        }

        private void zoomTextbox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                // Unit needs to be updated to match the current map window.
                this.UpdateMapZoom(Convert.ToDouble(this.zoomTextbox.Text), this.AssociatedWindow.ZoomUnit);
            }
        }

        private void zoomTextbox_Leave(object sender, EventArgs e)
        {
            // Unit needs to be updated to match the current map window.
            this.UpdateMapZoom(Convert.ToDouble(this.zoomTextbox.Text), this.AssociatedWindow.ZoomUnit);
        }

        private void YTextbox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && this.errorProvider.GetError((Control)sender) == "")
            {
                double y;
                double x;
                if (Double.TryParse(this.YCoordTextBox.Text, NumberStyles.Number, CultureInfo.CurrentCulture,out y) &&
                   Double.TryParse(this.XCoordTextBox.Text, NumberStyles.Number, CultureInfo.CurrentCulture, out x))
                {
                    this.UpdateMapWithNewCenters(x, y); 
                }
                               
            }
            if (e.KeyCode == Keys.Control && e.KeyCode == Keys.V)
            {
                ((TextBox)sender).Text = Clipboard.GetText();
            }
        }

        private void YTextbox_Leave(object sender, EventArgs e)
        {
            if (this.errorProvider.GetError((Control)sender) == "")
            {
                double y;
                double x;
                if (Double.TryParse(this.YCoordTextBox.Text, NumberStyles.Number, CultureInfo.CurrentCulture, out y) &&
                   Double.TryParse(this.XCoordTextBox.Text, NumberStyles.Number, CultureInfo.CurrentCulture, out x))
                {
                    this.UpdateMapWithNewCenters(x, y);
                }          
            }
        }

        /// <summary>
        /// Updates the centre of the map window with the supplied position.
        /// </summary>
        /// <param name="x">The X of the new center.</param>
        /// <param name="y">The Y of the new center.</param>
        public void UpdateMapWithNewCenters(double x, double y)
        {
            this.AssociatedWindow.UpdateCenters(x, y);
        }

        /// <summary>
        /// Updates the current zoom of the map.
        /// </summary>
        /// <param name="zoom">The zoom amount.</param>
        /// <param name="unit">The distance unit eg m,km etc</param>
        public void UpdateMapZoom(double zoom, string unit)
        {
            this.AssociatedWindow.UpdateZoom(zoom, unit);
        }

        private void refreshControls_Click(object sender, EventArgs e)
        {
            this.RefreshDataFromMapinfo();
        }

        public void RefreshDataFromMapinfo()
        {
            this.SyncMapbasicCoordSysToWindow();
            this.UpdateControlsWithValues();
        }

        /// <summary>
        /// Sets the mapbasic coordinate system to that of the current map window.
        /// </summary>
        public void SyncMapbasicCoordSysToWindow()
        {
            this.mapinfo.SessionCoordSys = this.AssociatedWindow.CoordSys;
            this.mapinfo.SessiongDistanceUnits = this.AssociatedWindow.ZoomUnit;
        }

        /// <summary>
        /// Updates all the textbox controls on the form with there values from the map.
        /// </summary>
        public void UpdateControlsWithValues()
        {
            int windowid = this.AssociatedWindow.ID;

            this.SetCenterTextBoxValues(this.AssociatedWindow.CenterX, this.AssociatedWindow.CenterY);
            this.SetZoomTextBoxValue(this.AssociatedWindow.Zoom);

            this.zoomunitlabel.Text = this.AssociatedWindow.ZoomUnit;
        }

        private void TextBox_TextChanged(object sender, EventArgs e)
        {
            double result;
            TextBox textbox = (TextBox)sender;
            bool correct = double.TryParse(textbox.Text, out result);
            if (!correct)
            {
                this.errorProvider.SetError(textbox, "Value not in correct format, can only be a number.");
            }
            else
            {
                this.errorProvider.SetError(textbox, "");
            }
        }

        private void AddFavourite_Click(object sender, EventArgs e)
        {
            AddFavouriteForm addfav = new AddFavouriteForm();
            addfav.ShowDialog(this);
            if (addfav.DialogResult == DialogResult.OK)
            {
                int windowid = this.AssociatedWindow.ID;
                Favourite fav = new Favourite();
                fav.Name = addfav.FavouriteName;

                // Make note of the current MapBasic Coordinate System.
                string oldcoordsys = this.mapinfo.SessionCoordSys;

                this.mapinfo.SessionCoordSys = this.AssociatedWindow.CoordSys;

                fav.X = this.AssociatedWindow.CenterX;
                fav.Y = this.AssociatedWindow.CenterY;
                fav.Zoom = this.AssociatedWindow.Zoom;
                fav.Units = this.AssociatedWindow.ZoomUnit;
                fav.Coordsys = this.AssociatedWindow.CoordSys;

                this.mapinfo.SessionCoordSys = oldcoordsys;

                this.SaveNewFavourite(fav);
            }
        }

        /// <summary>
        /// Saves the new favourite.
        /// </summary>
        /// <param name="fav">The favourite to be saved.</param>
        public void SaveNewFavourite(Favourite fav)
        {
            Globals.Favourites.Add(fav);
        }

        void favouriteClicked(object sender, EventArgs e)
        {
            MenuItem item = (MenuItem)sender;
            Favourite fav = (Favourite)item.Tag;

            // Before we do any work involving the map's X/Y coordinates, we
            // will set the current coordinate system; that way, we will guarantee 
            // that the coordinates will be processed correctly, regardless of which
            // coordinate system is in use in the current map. 
            // But, before we set the coordinate system, make note of the current 
            // coordinate system, so that we can restore it later.  This way, in
            // the unlikely event that the user typed a Set CoordSys statement into 
            // the MapBasic window, we will preserve the coordsys typed in by the user. 

            // Make note of the current MapBasic Coordinate System.
            string oldcoordsys = this.mapinfo.SessionCoordSys;

            // Set the coordsys clause to the csys that was saved with the named view 
            this.mapinfo.SessionCoordSys = fav.Coordsys;

            // Update the new map centers
            this.AssociatedWindow.UpdateCenters(fav.X, fav.Y);
            this.AssociatedWindow.UpdateZoom(fav.Zoom, fav.Units);

            // Restore the MapBasic Coordinate System to its previous state 
            this.mapinfo.SessionCoordSys = oldcoordsys;

        }

        private void MapOptions_Click(object sender, EventArgs e)
        {
            // Opens the map options dialog box.
            this.mapinfo.Do("Run Menu Command 802");

            // We call refresh so that it updates the coordsys and zoomunit for the current session just incase
            // something has changed.
            this.RefreshDataFromMapinfo();
        }
    }
}
